Skip to content

Prototype: instance scripts as dropped-in .csx - #2374

Draft
erwan-joly wants to merge 1 commit into
masterfrom
proto/instance-scripting
Draft

Prototype: instance scripts as dropped-in .csx#2374
erwan-joly wants to merge 1 commit into
masterfrom
proto/instance-scripting

Conversation

@erwan-joly

Copy link
Copy Markdown
Collaborator

Draft, to judge the authoring format before committing to one. Nothing here is wired into the server yet.

Why not a data format

Instance content is not declarative. The vocabulary these instances have always used is thirteen triggersOnDeath, OnTimeout, OnAreaEntry, OnLockerOpen, OnMapClean, OnFirstEnable … — and about twenty-six actions: SummonMonster, SpawnPortal, SetMonsterLockers, ChangePortalType, StartClock, RefreshRaidGoals, End.

That is an event tree. YAML or XML around it becomes an invented DSL with conditionals and sequencing — the GitHub-Actions problem — and neither gives any checking of the VNums and enums this codebase leans on.

Why .csx rather than Lua or JS

Sandboxing is the only axis where a scripting language wins, and it only matters if packs come from someone other than you. Against that, .csx gives type safety and IntelliSense over ~40 verbs, and — the part that decided it for me — scripts stay testable.

Worth knowing before reaching for Lua: MoonSharp, the library most people pick, last shipped in 2016. NLua is alive but needs per-platform native binaries; Jint is alive and pure-managed, and remains the right choice if untrusted content ever becomes a requirement. IInstanceRun is deliberately the only thing a script touches, so swapping engine is a binding change, not a redesign.

The startup objection, measured and removed

Roslyn scripting costs 1352 ms to warm up and ~85 ms per script after. Compiling ~114 instances at boot would be about eleven seconds for no benefit.

So scripts compile on first entry and are cached. Measured in CompilingIsPaidOnceThenCached: cold 63 ms, warm 0 ms. Invisible to the player who opens the door.

What is here

  • IInstanceRun — the verbs. This is the actual design; the engine behind it is swappable.
  • InstanceScript — a base so a script overrides only the triggers it uses, the way real definitions omit what they do not have.
  • CsxInstanceScriptLoader — lazy, cached, and fails loudly at load with the compiler diagnostics rather than opening a door onto an empty room.
  • instances/timespace-01.csx — a time-space in 47 lines: clock, three monsters behind a locker, portal unlocked on clear, boss, success.

Tests

Six, driving the dropped-in file against a recording run and asserting the exact sequence of calls — including that a broken script throws at load. This is the argument for csx: instance content is testable with the same harness as everything else, which for wave and timer logic is worth more than sandboxing.

Open questions

A prototype of the authoring half, to judge before committing to a format.

Instance content is not declarative. The vocabulary this game's instances have
always used is thirteen triggers - OnDeath, OnTimeout, OnAreaEntry, OnLockerOpen
and the rest - and about twenty-six actions: SummonMonster, SpawnPortal,
SetMonsterLockers, StartClock, End. That is an event tree, and XML or YAML would
each end up an invented DSL around it.

Scripts drop into instances/ as .csx and are compiled on first entry, never at
boot: Roslyn costs 1352ms to warm up and about 85ms a script afterwards, so
compiling all hundred-odd at startup would be eleven seconds for nothing.
Measured here, a cold load is 63ms and the second is 0ms.

IInstanceRun is the whole design. The script only ever calls those verbs, so the
engine behind it stays swappable - Jint or NLua would bind to the same interface
without remodelling anything.

Scripts are testable, which is the point of choosing csx over a scripting
language: CsxInstanceScriptTests drives the dropped-in file against a recording
run and asserts the exact sequence it asks for, including that a broken script
fails at load rather than opening a door onto an empty room.
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant